import { ONCES, isOnce } from "./once"
import { CallbackFunction, EventName } from "./types"
import { createRecorder } from "./record"

// May be extended in the future
// 后续可能扩展
export type EmitxOptions = {
    // Recorder instance
    // 记录器
    recorder?: ReturnType<typeof createRecorder>
    // Promise strategy for emit return value, default is allSettled
    // emit返回结果是采用Promise的哪个方法， 默认是allSettled
    promiseStrategy?: 'allSettled' | 'all' | 'race' | 'any' | 'no'
}

// Create event name object
// 创建事件名称对象
export interface eventNameObject {
    eventName: EventName
    options: {
        promiseStrategy?: EmitxOptions['promiseStrategy']
    }
}

// Resolve emit parameter passing
// 解决emit传递参数问题
export function createEventName(eventName: eventNameObject['eventName'], 
    options: eventNameObject['options'] = {}): eventNameObject{
    return {
        eventName,
        options
    }
}

// Main emitx function
// emitx
export function emitx(options:EmitxOptions = {}){
    // Internal protected variable
    // 内部保护变量
    const events = new Map<EventName,Set<CallbackFunction>>()

    // Get options
    // 获取参数
    const { recorder, promiseStrategy } = options

    // Get all callbacks
    // 获取所有callback
    function getAllCallback(){
        return [...events.values()].reduce((last, next) => {
            return last.concat(...next)
        },[])
    }

    // Delete marked functions
    // 删除标记函数
    function deleteOncesEvent(eventName: EventName, callback: CallbackFunction){
        const onces = (callback as any)[ONCES] as Set<EventName>
        // Handle once markers
        // 处理onces标记
        if(onces?.has(eventName)){
            // Unbind this event
            // 解绑这个事件
            off(eventName, callback)
            // Delete
            // 删除
            onces.delete(eventName)
            onces.size === 0 && delete (callback as any)[ONCES]
        }
    }

    // Emit event
    // emit
    async function emit(eventName: EventName | eventNameObject, ...reset:any[]){
        let options: eventNameObject['options']
        if(typeof eventName === 'object'){
            options = eventName?.options
            eventName = eventName?.eventName
        }
        if(!eventName) return
        let eventFns: Array<CallbackFunction>

        if(eventName === 'all') eventName = '*'
        // Handle all events
        // event all event
        if(eventName === '*'){
            eventFns = getAllCallback()
        }else{
            // Handle single event or all events
            // one event / all event
            eventFns = [...new Set([...(events.get(eventName)||[]), ...(events.get('*') || [])])]
        }
        recorder?.record('emit', eventName, eventFns);

        // Execute all callbacks
        // all callback
        const promises = eventFns.map(callback => {
            const result = async () => await callback(...reset);
            deleteOncesEvent(eventName, callback);
            return result();
        });
        
        // Handle different promise strategies
        switch(options?.promiseStrategy || promiseStrategy){
            case 'all':{
                return await Promise.all(promises)
            }
            case 'any':{
                return Promise.any(promises)
            }
            case 'allSettled':{
                return await Promise.allSettled(promises)
            }
            case 'race':{
                return await Promise.race(promises)
            }
            case 'no':{
                return promises
            }
            default:{
                return await Promise.allSettled(promises) 
            }
        }
    }

    // Register event listener
    // on
    function on(eventName:EventName, callback: CallbackFunction){
        if(!eventName) return
        recorder?.record('on', eventName, callback)
        if(eventName === 'all') eventName = '*'
        let eventFns = events.get(eventName)
        if(!eventFns) events.set(eventName,eventFns = new Set())
        eventFns.add(callback)
    }

    // Remove event listener
    // off
    function off(eventName?:EventName, callback?: CallbackFunction){
        recorder?.record('off', eventName, callback)
        if(!eventName) return events.clear()
        const eventFns = events.get(eventName)
        if(!eventFns) return
        if(!callback) return events.delete(eventName)
        return eventFns.delete(callback)
    }

    // -----extension----

    // Once: essentially adds a marker property
    // once 本质就是添加一个标记属性
    function once(eventName: EventName, callback: CallbackFunction) {
        recorder?.record('on', eventName, callback)
        const onces: Set<EventName> = (callback as any)[ONCES] || new Set()
        onces.add(eventName);
        (callback as any)[ONCES] = onces
        on(eventName, callback)
    }

    // Listen to all functions
    // 监听所有的函数
    function all(callback: CallbackFunction){
        recorder?.record('all', null, callback)
        return on('*', callback)
    }

    // Check if function is being listened to
    // 判断函数是否监听
    function isOn(callback: CallbackFunction){
        const allCallback = getAllCallback()
        return allCallback.includes(callback)
    }

    // Get all event names
    // 获取所有事件名称
    function getEventNames(){
        return events.keys()
    }

    // Get complete events storage object (copy)
    // 获取总事件存储对象（copy）
    function getEvents(){
        const result = new Map<EventName,Set<CallbackFunction>>()
        events.forEach((callbacks,eventName) => {
            result.set(eventName, new Set(callbacks))
        })
        return result
    }
    
    return {
        off,
        on,
        emit,
        once,
        all,
        getEventNames,
        getAllCallback,
        getEvents,
        isOn,
        isOnce,
    }
}